package com.ai.aspect;

import com.ai.common.ParentResult;
import com.ai.common.email.EmailClient;
import com.ai.common.enums.ConfigModuleEnum;
import com.ai.common.gpt.subscriber.OpenAIMessageResponse;
import com.ai.common.http.WebClient;
import com.ai.common.http.WeiXinApiClient;
import com.ai.common.http.dto.GetAddresseResponse;
import com.ai.common.utils.ConfigUtil;
import com.ai.common.utils.TraceContext;
import com.ai.dao.entity.ai.AiSensitiveWordRecord;
import com.ai.dao.entity.ai.AiUser;
import com.ai.dao.entity.ai.AiUserAccessRecord;
import com.ai.repository.ai.AiSensitiveWordRecordRepository;
import com.ai.repository.ai.AiUserAccessRecordRepository;
import com.ai.repository.ai.AiUserRepository;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import reactor.core.publisher.Flux;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * 积分认证切面
 */
@Slf4j
@Order(1)
@Aspect
@Component
public class IntegralAuthAspect {

    @Autowired
    private AiUserRepository aiUserRepository;
    @Autowired
    private AiUserAccessRecordRepository aiUserAccessRecordRepository;
    @Autowired
    private AiSensitiveWordRecordRepository aiSensitiveWordRecordRepository;
    @Autowired
    private WebClient webClient;
    @Autowired
    private EmailClient emailClient;
    @Autowired
    private WeiXinApiClient weiXinApiClient;

    @Pointcut("@annotation(com.ai.aspect.IntegralAuth)")
    public void aspect() {}

    @Around("aspect()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Type returnType = null;
        int code = 1;
        //请求路径
        String requestURI = null;
        try {
            String uid = null;
            //获取请求头和请求参数
            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            HttpServletRequest request = null;
            if (sra != null) {
                request = sra.getRequest();
                requestURI = request.getRequestURI();
                // 获取请求头
                Enumeration<String> enumeration = request.getHeaderNames();
                while (enumeration.hasMoreElements()) {
                    String name = enumeration.nextElement();
                    String value = request.getHeader(name);
                    if ("uid".equals(name)){
                        uid = value;
                        break;
                    }
                }
            }
            //////////////////////////////////////////////
            String msg = "网络异常，请稍后重试！";
            String optionType = null;
            String content = null;
            String describe = null;
            //获取方法返回值类型
            Object[] args = joinPoint.getArgs();
            Class<?>[] paramsCls = new Class<?>[args.length];
            for (int i = 0; i < args.length; ++i) {
                //注意 get方法少传参有问题
                paramsCls[i] = args[i].getClass();
                //反射获取入参bean的字段
                //得到属性
                Field field = args[i].getClass().getDeclaredField("optionType");
                //打开私有访问
                field.setAccessible(true);
                //获取属性值
                optionType = (String) field.get(args[i]);
                //获取属性名
                //String name = field.getName();
                //设置值
                //field.set(args[i],uid);
                Field contentField = args[i].getClass().getDeclaredField("content");
                //打开私有访问
                contentField.setAccessible(true);
                content = (String) contentField.get(args[i]);
                Field describeField = args[i].getClass().getDeclaredField("describe");
                //打开私有访问
                describeField.setAccessible(true);
                describe = (String) describeField.get(args[i]);
            }
            //获取方法
            Method method = joinPoint.getTarget().getClass().getMethod(joinPoint.getSignature().getName(), paramsCls);
            //获取返回值类型
            returnType = method.getAnnotatedReturnType().getType();
            //uid为空时返回
            if (StringUtils.isBlank(uid)) {
                log.warn("uid不能为空");
                if ("reactor.core.publisher.Flux<java.lang.String>".equals(returnType.getTypeName())){
                    return Flux.just(JSON.toJSONString(new OpenAIMessageResponse(msg, null)), JSON.toJSONString(new OpenAIMessageResponse("", true)));
                }else {
                    return getResultInstance(returnType,code,msg);
                }
            }
            String aiUserStr = TraceContext.getCurrent().getTag("aiUser", String.class);
            //String uidStr = TraceContext.getCurrent().getTag("uid", String.class);
            if (StringUtils.isBlank(aiUserStr)){
                log.warn("aiUserStr is null " + uid);
                return getResultInstance(returnType,2,msg);
            }

            //限制只能白名单用户才能使用
            boolean showCard = ConfigUtil.getInstance().isShowCard(uid);
            if (!showCard){
                return getResultInstance(returnType,code,ConfigUtil.getInstance().getConfig(ConfigModuleEnum.aiCard.getModule(), "cardBlackListHint"));
            }

            //获取积分次数
            AiUser aiUser = JSON.parseObject(aiUserStr,AiUser.class);
            if (aiUser == null){
                log.warn("用户不存在，uid=" + uid);
                return getResultInstance(returnType,2,msg);
            }
            if (aiUser.getIntegral() == null || aiUser.getIntegral() <= -100){
                log.warn("用户积分不足，uid=" + uid);
                //积分不足提示
                String integralLack = ConfigUtil.getInstance().getConfig(ConfigModuleEnum.integral.getModule(), "integralLack");
                return getResultInstance(returnType,0,null,integralLack);
            }
            //内容违规校验
            String checkContent = content;
            if (StringUtils.isNotBlank(describe)){
                checkContent = describe + "," + checkContent;
            }
            Object msgSecCheck = msgSecCheck(returnType, aiUser.getOpenId(), checkContent,uid,optionType,requestURI);
            if (msgSecCheck != null){
                return msgSecCheck;
            }

            String getOptionType = ConfigUtil.getInstance().getOptionType(optionType);
            if (StringUtils.isBlank(getOptionType)){
                return getResultInstance(returnType,code,"选项类型有误");
            }
            //获取请求参数
            Map<String, Object> parameterMap = new HashMap<>();
            String[] names = ((CodeSignature) joinPoint.getSignature()).getParameterNames();
            for (int i = 0; i < names.length; i++) {
                parameterMap.put(names[i], args[i]);
            }

            // 业务方法
            Object res = joinPoint.proceed();

            //返回成功并且无异常则减积分
            ParentResult parentResult = JSON.parseObject(JSON.toJSONString(res), ParentResult.class);
            if (parentResult != null && parentResult.getCode() == 0){
                aiUserRepository.updateIntegral(uid,-1);
            }
            //记录客户端信息
            insertClientInfo(request,parameterMap,res,requestURI,uid,optionType);
            return res;
        }catch (Exception e){
            log.error(e.getMessage(),e);
            chatGPTErrorSendEmail(requestURI + "接口调用异常",e.getMessage());
            return getResultInstance(returnType,code,"系统异常，请稍后重试！");
        }
    }

    /**
     * 内容违规校验
     * @param returnType
     * @param openId
     * @param content
     * @return
     * @throws Exception
     */
    private Object msgSecCheck(Type returnType, String openId, String content,String uid,String optionType,String requestUrl) throws Exception {
        Object response = null;
        if (StringUtils.isNotBlank(content)){
            content = content.toLowerCase();
            int code = 1;
            //敏感词校验
//            WxMsgSecCheckResponse wxMsgSecCheckResponse = weiXinApiClient.wxMsgSecCheck(openId, content);
//            if(wxMsgSecCheckResponse == null || wxMsgSecCheckResponse.getErrcode() == null || wxMsgSecCheckResponse.getErrcode() != 0){
//                return getResultInstance(returnType,code,"网络异常，请稍后重试！");
//            }
//            if (wxMsgSecCheckResponse.getResult() != null){
//                WxMsgSecCheckResultDto result = wxMsgSecCheckResponse.getResult();
//                if (result.getLabel() == null || !result.getLabel().equals(100)){
//                    //违规词
//                    response = getResultInstance(returnType,code,"内容包含敏感信息，请整改");
//                }
//            }
            if (content.contains("gpt") || content.contains("chat")){
                response = getResultInstance(returnType,code,"内容违规，请整改");
            }
        }
        if (response != null) {
            //保存违规内容
            AiSensitiveWordRecord aiSensitiveWordRecord = new AiSensitiveWordRecord();
            aiSensitiveWordRecord.setUid(uid);
            aiSensitiveWordRecord.setContent(content);
            aiSensitiveWordRecord.setOptionType(optionType);
            aiSensitiveWordRecord.setRequestUrl(requestUrl);
            aiSensitiveWordRecordRepository.insertSelective(aiSensitiveWordRecord);
        }
        return response;
    }
    //异常发送邮件
    private void chatGPTErrorSendEmail(String subject,String error) {
        String chatGPTErrorToEmails = ConfigUtil.getInstance().getConfig(ConfigModuleEnum.email.getModule(), "chatGPTErrorToEmails");
        if (StringUtils.isNotBlank(chatGPTErrorToEmails)) {
            String[] chatGPTErrorToEmailsArray = chatGPTErrorToEmails.split(",");
            List<String> toEmails = new ArrayList<>(chatGPTErrorToEmailsArray.length);
            //数组转list
            Collections.addAll(toEmails, chatGPTErrorToEmailsArray);
            emailClient.send(subject, "异常：" + error, toEmails);
        }
    }


    /**
     * 通过反射实例化返回参数并设置code和msg属性
     * @param returnType
     * @param code
     * @param msg
     * @return
     * @throws Exception
     */
    private Object getResultInstance(Type returnType,int code,String msg) throws Exception {
        return getResultInstance(returnType,code,msg,null);
    }
    /**
     * 通过反射实例化返回参数并设置code和msg属性
     * @param returnType
     * @param code
     * @param msg
     * @return
     * @throws Exception
     */
    private Object getResultInstance(Type returnType,int code,String msg,String content) throws Exception {
        if (returnType == null){
            ParentResult parentResult = new ParentResult();
            parentResult.setCode(code);
            parentResult.setMsg(msg);
            return parentResult;
        }
        //通过反射实例化返回对象
        Object newInstance = Class.forName(returnType.getTypeName()).newInstance();
        //获取返回对象的setMsg()方法
        Method msgMethod = newInstance.getClass().getMethod("setMsg", String.class);
        Method codeMethod = newInstance.getClass().getMethod("setCode", Integer.class);
        //设置返回值
        msgMethod.invoke(newInstance,msg);
        codeMethod.invoke(newInstance,code);
        if (StringUtils.isNotBlank(content)) {
            Method contentMethod = newInstance.getClass().getMethod("setContent", String.class);
            contentMethod.invoke(newInstance, content);
        }
        return newInstance;
    }

    /**
     * 记录客户端信息
     * @param request
     * @return
     */
    @Async
    public void insertClientInfo(HttpServletRequest request,Map<String, Object> parameterMap,Object res,String requestURI,String uid,String optionType){
        AiUserAccessRecord aiUserAccessRecord = new AiUserAccessRecord();
        if (request == null){
            return;
        }
        try {
            String equipment = null;
            String userAgent = request.getHeader("user-agent");
            if (userAgent.contains("Android")) {
                equipment = "Android";
            } else if (userAgent.contains("iPhone")) {
                equipment = "iPhone";
            } else if (userAgent.contains("iPad")) {
                equipment = "iPad";
            } else {
                equipment = "pc";
            }
            String ip = request.getHeader("X-Forwarded-For");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
                ip = request.getHeader("Proxy-Client-IP");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
                ip = request.getHeader("WL-Proxy-Client-IP");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
                ip = request.getHeader("HTTP_CLIENT_IP");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip))
                ip = request.getRemoteAddr();
            if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)){
                try {
                    ip = InetAddress.getLocalHost().getHostAddress();
                } catch (UnknownHostException unknownhostexception) {
                    log.warn(unknownhostexception.getMessage(), unknownhostexception);
                }
            }
            aiUserAccessRecord.setIp(ip);
            aiUserAccessRecord.setEquipment(equipment);
            aiUserAccessRecord.setRecord(userAgent);
            aiUserAccessRecord.setDateTime(new Date());
            try {
                //根据ip获取地址
                if (StringUtils.isNotBlank(ip)){
                    String result = webClient.get("https://sp1.baidu.com/8aQDcjqpAAV3otqbppnN2DJv/api.php?query=" + ip + "&resource_id=5809");
                    if (StringUtils.isNotBlank(result)){
                        GetAddresseResponse addresseResponse = JSON.parseObject(result,GetAddresseResponse.class);
                        if (addresseResponse != null && CollectionUtils.isNotEmpty(addresseResponse.getData())) {
                            aiUserAccessRecord.setAddresse(addresseResponse.getData().get(0).getLocation());
                        }
                    }
                }
            }catch (Exception e){
                log.warn(e.getMessage(),e);
            }
            aiUserAccessRecord.setUid(uid);
            aiUserAccessRecord.setRequestUrl(requestURI);
            aiUserAccessRecord.setRequest(JSON.toJSONString(parameterMap));
            aiUserAccessRecord.setOptionType(optionType);
            aiUserAccessRecord.setResponse(JSON.toJSONString(res));
            aiUserAccessRecordRepository.insertSelective(aiUserAccessRecord);
        } catch (Exception e) {
            log.warn(e.getMessage(), e);
        }
    }
}
